﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Linq;
using Model;

namespace WindowsTest
{
    /// <summary>
    /// 影像纠偏类
    /// </summary>
    class ImageCorrect
    {
        #region 影像纠偏方法

        /// <summary>
        /// 影像纠偏方法
        /// </summary>
        /// <param name="bm">输入的影像</param>
        /// <returns>纠偏后的影像</returns>
        public static Bitmap ImageRectification(Bitmap bm)
        {
            var w = bm.Width;
            var h = bm.Height;
            var im = Common.ToBinary(bm, Matlab.GetThreshold(bm));
            var hLines = ImageAnalysis.FindHorizontalLines(im, 8, w / 5, w);//找横线
            var vLines = ImageAnalysis.FindVerticalLines(im, 8, h / 8, h);//找竖线
            var degree = GetDegree(hLines, vLines);
            var d = Math.Round(degree / Math.PI * 180, 2);
            var obm = RotateImg(bm, d);
            return obm;
        }

        /// <summary>
        /// 图像旋转
        /// </summary>
        /// <param name="bm">影像</param>
        /// <param name="angle">旋转角度</param>
        /// <returns>旋转后的影像</returns>
        public static Bitmap RotateImg(Bitmap bm, double angle)
        {
            angle = angle % 360;

            //弧度转换
            double radian = angle * Math.PI / 180.0;
            double cos = Math.Cos(radian);
            double sin = Math.Sin(radian);

            //原图的宽和高
            int w = bm.Width;
            int h = bm.Height;
            var W = (int)(Math.Max(Math.Abs(w * cos - h * sin), Math.Abs(w * cos + h * sin)));
            var H = (int)(Math.Max(Math.Abs(w * sin - h * cos), Math.Abs(w * sin + h * cos)));

            //目标位图
            var dsImage = new Bitmap(W, H);
            dsImage = Common.GetBitmap(dsImage, 255);
            Graphics g = Graphics.FromImage(dsImage);
            g.InterpolationMode = InterpolationMode.Bilinear;
            g.SmoothingMode = SmoothingMode.HighQuality;

            //计算偏移量
            var offset = new Point((W - w) / 2, (H - h) / 2);

            //构造图像显示区域：让图像的中心与窗口的中心点一致
            var rect = new Rectangle(offset.X, offset.Y, w, h);
            var center = new Point(rect.X + rect.Width / 2, rect.Y + rect.Height / 2);

            g.TranslateTransform(center.X, center.Y);
            g.RotateTransform((float)(360 - angle));

            //恢复图像在水平和垂直方向的平移
            g.TranslateTransform(-center.X, -center.Y);
            g.DrawImage(bm, rect);

            //重至绘图的所有变换
            g.ResetTransform();

            g.Save();
            g.Dispose();
            //保存旋转后的图片
            bm.Dispose();

            return dsImage;
        }

        /// <summary>
        /// 分析影像偏向角度
        /// </summary>
        /// <param name="hLines">直线集合</param>
        /// <param name="vLines">竖线集合</param>
        /// <returns>偏向角度（向下为正，向上为负）</returns>
        public static double GetDegree(List<Line> hLines, List<Line> vLines)
        {
            var degreeArry = new List<double>();//角度列表
            var direction = new List<int>();//判断图像旋转方向
            var listClusterLines1 = new List<List<Line>>();
            var listClusterLines2 = new List<List<Line>>();
            var hCount = hLines.Count;
            var vCount = vLines.Count;
            if (hLines.Count > 0)
            {
                listClusterLines1 = findClusterLines(hLines);
            }
            if (vLines.Count > 0)
            {
                listClusterLines2 = findClusterLines(vLines);
            }

            if (listClusterLines1.Count > 0 && hCount / listClusterLines1.Count < 100 && hCount / listClusterLines1.Count > 15)
            {
                foreach (var listClusterLine in listClusterLines1)
                {
                    if (listClusterLine.Count < 15 || listClusterLine.Count > 60)
                    {
                        continue;
                    }
                    var min = listClusterLine[0].B_Max - listClusterLine[0].B_Min;
                    var minLine = listClusterLine[0];
                    foreach (var line in listClusterLine)
                    {
                        if (min > line.B_Max - line.B_Min)
                        {
                            min = line.B_Max - line.B_Min;
                            minLine = line;
                        }
                    }

                    var max = listClusterLine[0].B_Max - listClusterLine[0].B_Min;
                    var maxLine = listClusterLine[0];
                    foreach (var line in listClusterLine)
                    {
                        if (max < line.B_Max - line.B_Min)
                        {
                            max = line.B_Max - line.B_Min;
                            maxLine = line;
                        }
                    }

                    if (minLine.A < maxLine.A)
                    {
                        direction.Add(-1);
                    }
                    else
                    {
                        direction.Add(1);
                    }

                    //分析角度
                    var a = listClusterLine.Count;
                    var b = max - min;
                    double d = Math.Atan((double)a / b);
                    degreeArry.Add(d);
                }
            }

            if (listClusterLines2.Count > 0 && vCount / listClusterLines2.Count < 100 && vCount / listClusterLines2.Count > 15)
            {
                foreach (var listClusterLine in listClusterLines2)
                {
                    if (listClusterLine.Count < 15 || listClusterLine.Count > 100)
                    {
                        continue;
                    }
                    var min = listClusterLine[0].B_Max - listClusterLine[0].B_Min;
                    var minLine = listClusterLine[0];
                    foreach (var line in listClusterLine)
                    {
                        if (min > line.B_Max - line.B_Min)
                        {
                            min = line.B_Max - line.B_Min;
                            minLine = line;
                        }
                    }

                    var max = listClusterLine[0].B_Max - listClusterLine[0].B_Min;
                    var maxLine = listClusterLine[0];
                    foreach (var line in listClusterLine)
                    {
                        if (max < line.B_Max - line.B_Min)
                        {
                            max = line.B_Max - line.B_Min;
                            maxLine = line;
                        }
                    }

                    if (minLine.A > maxLine.A)
                    {
                        direction.Add(-1);
                    }
                    else
                    {
                        direction.Add(1);
                    }

                    //分析角度
                    var a = listClusterLine.Count;
                    var b = max - min;
                    double d = Math.Atan((double)a / b);
                    degreeArry.Add(d);
                }
            }

            if (degreeArry.Count < 3)
            {
                return 0;
            }

            double s = 0.00d;
            for (int i = 0; i < degreeArry.Count; i++)
            {
                s += degreeArry[i];
            }

            if (degreeArry.Count > 0)
            {
                double degree = Math.Round(s / degreeArry.Count, 2);

                //超过5度不予理会
                if (Math.Abs(degree) > 5)
                {
                    return 0;
                }

                var v = 0;
                for (int i = 0; i < direction.Count; i++)
                {
                    v += direction[i];
                }

                if (v < 0)
                {
                    return -degree;
                }

                return degree;
            }

            return 0;
        }

        /// <summary>
        /// 分析影像偏向角度
        /// </summary>
        /// <param name="hLines">直线集合</param>
        /// <param name="vLines">竖线集合</param>
        /// <returns>偏向角度（向下为正，向上为负）</returns>
        public static double GetDegree2(List<Line> hLines, List<Line> vLines)
        {
            var degreeArry = new List<double>();//角度列表
            var listClusterLines1 = new List<List<Line>>();
            var listClusterLines2 = new List<List<Line>>();
            if (hLines.Count > 0)
            {
                listClusterLines1 = findClusterLines(hLines);
            }
            if (vLines.Count > 0)
            {
                listClusterLines2 = findClusterLines(vLines);
            }

            foreach (var listClusterLine in listClusterLines1)
            {
                var min = listClusterLine[0].B_Max - listClusterLine[0].B_Min;
                var minLine = listClusterLine[0];
                foreach (var line in listClusterLine)
                {
                    if (min > line.B_Max - line.B_Min)
                    {
                        min = line.B_Max - line.B_Min;
                        minLine = line;
                    }
                }

                var max = listClusterLine[0].B_Max - listClusterLine[0].B_Min;
                var maxLine = listClusterLine[0];
                foreach (var line in listClusterLine)
                {
                    if (max < line.B_Max - line.B_Min)
                    {
                        max = line.B_Max - line.B_Min;
                        maxLine = line;
                    }
                }

                //角度偏向系数：正为顺时针方向，负为逆时针方向
                var coe = 0;
                if (minLine.A < maxLine.A)
                {
                    coe = -1;
                }
                else if (minLine.A > maxLine.A)
                {
                    coe = 1;
                }

                if (coe != 0)
                {
                    //分析角度
                    var a = Math.Abs(maxLine.A - minLine.A);
                    var b = max - min;
                    double d = Math.Atan((double)a / b);
                    degreeArry.Add(d * coe);
                }
            }

            foreach (var listClusterLine in listClusterLines2)
            {
                var min = listClusterLine[0].B_Max - listClusterLine[0].B_Min;
                var minLine = listClusterLine[0];
                foreach (var line in listClusterLine)
                {
                    if (min > line.B_Max - line.B_Min)
                    {
                        min = line.B_Max - line.B_Min;
                        minLine = line;
                    }
                }

                var max = listClusterLine[0].B_Max - listClusterLine[0].B_Min;
                var maxLine = listClusterLine[0];
                foreach (var line in listClusterLine)
                {
                    if (max < line.B_Max - line.B_Min)
                    {
                        max = line.B_Max - line.B_Min;
                        maxLine = line;
                    }
                }

                //角度偏向系数：正为顺时针方向，负为逆时针方向
                var coe = 0;
                if (minLine.A < maxLine.A)
                {
                    coe = -1;
                }
                else if (minLine.A > maxLine.A)
                {
                    coe = 1;
                }

                if (coe != 0)
                {
                    //分析角度
                    var a = Math.Abs(maxLine.A - minLine.A);
                    var b = max - min;
                    double d = Math.Atan((double)a / b);
                    degreeArry.Add(d * coe);
                }
            }

            //角度个数少于三个的，认为是不精确的角度，不予理会
            if (degreeArry.Count < 3)
            {
                return 0;
            }

            var arrLargeZero = new List<double>();
            var arrSmallZero = new List<double>();

            foreach (var d in degreeArry)
            {
                if (d > 0.0)
                {
                    arrLargeZero.Add(d);
                }
                else
                {
                    arrSmallZero.Add(d);
                }
            }

            if (arrLargeZero.Count > arrSmallZero.Count)
            {

                double s = 0.00d;
                for (int i = 0; i < arrLargeZero.Count; i++)
                {
                    s += arrLargeZero[i];
                }

                double aveF = Math.Round(s / arrLargeZero.Count, 3);//第一次平均值

                //角度分组
                //每个角度除以平均值afeF，商大于1的分成一组，小于1的分成一组，等于1的分配到这两个组
                var arrBigger = new List<double>();
                var arrLess = new List<double>();
                foreach (var d in arrLargeZero)
                {
                    if (d / aveF > 1)
                    {
                        arrBigger.Add(d);
                    }
                    else if (d / aveF < 1)
                    {
                        arrLess.Add(d);
                    }
                    else
                    {
                        arrBigger.Add(d);
                        arrLess.Add(d);
                    }
                }

                //以角度数多的一组为准,如果角度数相等，则直接启用平均值
                if (arrBigger.Count > arrLess.Count)
                {
                    var sum = 0.0d;
                    foreach (var d in arrBigger)
                    {
                        sum += d;
                    }

                    //再次求平均值，同样与每项与平均值相除，去除商大于1的角度
                    var aveS = sum / arrBigger.Count;
                    var arrFinal = new List<double>();
                    foreach (var d in arrBigger)
                    {
                        if (d / aveS < 1)
                        {
                            arrFinal.Add(d);
                        }
                    }

                    var sum3 = 0.0d;
                    foreach (var d in arrFinal)
                    {
                        sum3 += d;
                    }
                    var degreeFinal = sum3 / arrFinal.Count;

                    //超过5度不予理会
                    if (Math.Abs(degreeFinal * 180 / Math.PI) > 10)
                    {
                        return 0;
                    }
                    return degreeFinal;
                }
                else
                {
                    var sum = 0.0d;
                    foreach (var d in arrLess)
                    {
                        sum += d;
                    }

                    //再次求平均值，同样与每项与平均值相除，去除商大于1的角度
                    var aveS = sum / arrLess.Count;
                    var arrFinal = new List<double>();
                    foreach (var d in arrLess)
                    {
                        if (d / aveS > 1)
                        {
                            arrFinal.Add(d);
                        }
                    }

                    var sum3 = 0.0d;
                    foreach (var d in arrFinal)
                    {
                        sum3 += d;
                    }
                    var degreeFinal = sum3 / arrFinal.Count;

                    //超过5度不予理会
                    if (Math.Abs(degreeFinal * 180 / Math.PI) > 10)
                    {
                        return 0;
                    }
                    return degreeFinal;
                }
            }
            if (arrSmallZero.Count > arrLargeZero.Count)
            {

                double s = 0.00d;
                for (int i = 0; i < arrSmallZero.Count; i++)
                {
                    s += arrSmallZero[i];
                }

                double aveF = Math.Round(s / arrSmallZero.Count, 3);//第一次平均值

                //角度分组
                //每个角度除以平均值afeF，商大于1的分成一组，小于1的分成一组，等于1的分配到这两个组
                var arrBigger = new List<double>();
                var arrLess = new List<double>();
                foreach (var d in arrSmallZero)
                {
                    if (d / aveF > 1)
                    {
                        arrBigger.Add(d);
                    }
                    else if (d / aveF < 1)
                    {
                        arrLess.Add(d);
                    }
                    else
                    {
                        arrBigger.Add(d);
                        arrLess.Add(d);
                    }
                }

                //以角度数多的一组为准,如果角度数相等，则直接启用平均值
                if (arrBigger.Count > arrLess.Count)
                {
                    var sum = 0.0d;
                    foreach (var d in arrBigger)
                    {
                        sum += d;
                    }

                    //再次求平均值，同样与每项与平均值相除，去除商大于1的角度
                    var aveS = sum / arrBigger.Count;
                    var arrFinal = new List<double>();
                    foreach (var d in arrBigger)
                    {
                        if (d / aveS < 1)
                        {
                            arrFinal.Add(d);
                        }
                    }

                    var sum3 = 0.0d;
                    foreach (var d in arrFinal)
                    {
                        sum3 += d;
                    }
                    var degreeFinal = sum3 / arrFinal.Count;

                    //超过5度不予理会
                    if (Math.Abs(degreeFinal * 180 / Math.PI) > 10)
                    {
                        return 0;
                    }
                    return degreeFinal;
                }
                else
                {
                    var sum = 0.0d;
                    foreach (var d in arrLess)
                    {
                        sum += d;
                    }

                    //再次求平均值，同样与每项与平均值相除，去除商大于1的角度
                    var aveS = sum / arrLess.Count;
                    var arrFinal = new List<double>();
                    foreach (var d in arrLess)
                    {
                        if (d / aveS > 1)
                        {
                            arrFinal.Add(d);
                        }
                    }

                    var sum3 = 0.0d;
                    foreach (var d in arrFinal)
                    {
                        sum3 += d;
                    }
                    var degreeFinal = sum3 / arrFinal.Count;

                    //超过5度不予理会
                    if (Math.Abs(degreeFinal * 180 / Math.PI) > 10)
                    {
                        return 0;
                    }
                    return degreeFinal;
                }
            }
            return 0;
        }

        /// <summary>
        /// 找同簇直线
        /// </summary>
        /// <param name="lines">直线集合</param>
        /// <returns>同簇直线列表</returns>
        private static List<List<Line>> findClusterLines(List<Line> lines)
        {
            var listClusterLines = new List<List<Line>>();
            for (int i = 0; i < lines.Count; i++)
            {
                var custerLines = new List<Line> { lines[i] };
                findLinesCluster(ref lines, ref custerLines, lines[i]);
                listClusterLines.Add(custerLines);
                lines.Remove(lines[i]);
            }
            return listClusterLines;
        }

        /// <summary>
        /// 找同簇直线
        /// </summary>
        /// <param name="lines">直线集合</param>
        /// <param name="clusterLines">同簇直线集合 </param>
        /// <param name="tempLine"> 模板直线</param>
        /// <returns>直线簇</returns>
        private static void findLinesCluster(ref List<Line> lines, ref List<Line> clusterLines, Line tempLine)
        {
            var afterLines = lines.FindAll(l => l.A == tempLine.A + 1);
            if (afterLines.Any())
            {
                var waitFixLines = new List<Line>();
                foreach (var afterLine in afterLines)
                {
                    if (ImageAnalysis.IsOnSameCluster(tempLine, afterLine))
                    {
                        waitFixLines.Add(afterLine);
                    }
                }

                if (waitFixLines.Count >= 2)
                {
                    var newLine = fixLine(waitFixLines);
                    clusterLines.Add(newLine);
                    lines.Remove(tempLine);
                    findLinesCluster(ref lines, ref clusterLines, newLine);
                }
                else if (waitFixLines.Count == 1)
                {
                    clusterLines.Add(waitFixLines[0]);
                    lines.Remove(tempLine);
                    findLinesCluster(ref lines, ref clusterLines, waitFixLines[0]);
                }
            }
        }

        /// <summary>
        /// 修复直线
        /// </summary>
        /// <param name="lines">同直线上的直线集合</param>
        /// <returns>修复后的直线</returns>
        private static Line fixLine(List<Line> lines)
        {
            var newLine = new Line { A = lines[0].A };
            var min = lines[0].B_Min;
            foreach (var line in lines)
            {
                if (min > line.B_Min)
                {
                    min = line.B_Min;
                }
            }
            var max = lines[0].B_Max;
            foreach (var line in lines)
            {
                if (max < line.B_Max)
                {
                    max = line.B_Max;
                }
            }

            newLine.B_Min = min;
            newLine.B_Max = max;

            return newLine;
        }

        /// <summary>
        /// 通过角度正切值得到角度数，单位度
        /// </summary>
        /// <param name="tanA">正切值</param>
        /// <returns>角度数，单位度</returns>
        public static double GetDegree(double tanA)
        {
            return Math.Round((180 * Math.Atan(tanA)) / Math.PI, 2);
        }

        #endregion
    }
}
